package com.sprouting.ops.exception;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import com.sprouting.ops.enums.ResultEnum;
import com.sprouting.ops.model.ResultJson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;

/**
 * 全局异常处理类，只针对控制器层的异常
 *
 * @author ：LX（长沙麓谷）
 * @date 创建时间： 2023/3/16 22:21
 */
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {


    /**
     * 默认异常处理方法
     * @param e 错误
     * @return 返回异常请求路径和异常信息
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ResultJson defaultErrorHandler(HttpServletRequest request, Exception e){
        log.error("1、默认异常处理,异常请求路径：{},异常：", request.getRequestURI(), e);
        return new ResultJson<>(false, ResultEnum.ERROR.getMsg(), null, ResultEnum.ERROR.getCode());
    }

    /**
     * 默认的业务异常
     */
    @ExceptionHandler(value = ServiceException.class)
    @ResponseBody
    public ResultJson jsonErrorHandler(HttpServletRequest request, ServiceException e) {
        log.warn("2、异常请求路径：{},异常错误信息:{}", request.getRequestURI(), e.getMsg());
        return new ResultJson<>(false, e.getMsg(), null, ResultEnum.FAIL.getCode());
    }

    /**
     * 最高级别的错误拦截，防止错误信息进入到前端
     * @param request 请求
     * @param e 错误信息
     * @return
     */
    @ExceptionHandler(value = Throwable.class)
    @ResponseBody
    public ResultJson throwableHandler(HttpServletRequest request, Throwable e){
        log.error("3、异常请求路径：{},Throwable错误信息:", request.getRequestURI(), e);
        return new ResultJson<>(false, ResultEnum.ERROR.getMsg(), null, ResultEnum.ERROR.getCode());
    }

    /**
     * 权限异常，未登录
     * @param request 请求
     * @param e 错误信息
     * @return
     */
    @ExceptionHandler(value = NotLoginException.class)
    @ResponseBody
    public ResultJson handlerNotLoginException(HttpServletRequest request, NotLoginException e){
        log.error("4、权限异常, 请求路径:{},错误信息：{}", request.getRequestURI(), e.getMessage());
        String errorMsg;

        if (NotLoginException.NOT_TOKEN.equals(e.getType())){
            //未能从请求中读取到token
            errorMsg = NotLoginException.NOT_TOKEN_MESSAGE;

        } else if (NotLoginException.INVALID_TOKEN.equals(e.getType())){
            //已读取到token，但是token无效
            errorMsg = "请求令牌无效，请重新登录";

        } else if (NotLoginException.TOKEN_TIMEOUT.equals(e.getType())){
            //已读取到token，但是token已经过期
            errorMsg = "令牌已过期，请重新登录";

        } else if (NotLoginException.BE_REPLACED.equals(e.getType())){
            //已读取到token，但是token已被顶下线
            errorMsg = "用户已在其他地方登陆";

        } else if (NotLoginException.KICK_OUT.equals(e.getType())){
            //已读取到token，但是token已被踢下线
            errorMsg = "用户已被强制下线";

        } else {
            errorMsg = "当前会话未登录";
        }

        return new ResultJson<>(false, errorMsg, null, ResultEnum.NOT_LOGIN.getCode());
    }

    /**
     * 权限异常-无角色
     * @param request 请求
     * @param e 错误信息
     * @return
     */
    @ExceptionHandler(value = NotRoleException.class)
    @ResponseBody
    public ResultJson notRoleExceptionHandler(HttpServletRequest request, NotRoleException e){
        log.error("5、[权限错误] 无此角色：{}，请求路径：{}", e.getRole(), request.getRequestURI());
        return ResultJson.noPermission();
    }

    /**
     * 权限异常-无权限
     * @param request 请求
     * @param e 错误信息
     * @return
     */
    @ExceptionHandler(value = NotPermissionException.class)
    @ResponseBody
    public ResultJson notRoleExceptionHandler(HttpServletRequest request, NotPermissionException e){
        log.error("6、[权限错误] 无此权限：{},请求路径：{}", e.getPermission(), request.getRequestURI());
        return ResultJson.noPermission();
    }

    /**
     * 对于注解校验的进行统一的验证处理
     * @param request 请求
     * @param e 错误信息
     * @return
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    @ResponseBody
    public ResultJson methodArgumentNotValidExceptionHandler(HttpServletRequest request, MethodArgumentNotValidException e){
        log.warn("7、数据校验不通过，请求路径：{},异常错误信息:{}", request.getRequestURI(), e.getMessage());
        return ResultJson.error(getErrorMsg(e));
    }

    /**
     * 对于注解校验的进行统一的验证处理
     * @param request 请求
     * @param e 错误信息
     * @return
     */
    @ExceptionHandler(value = BindException.class)
    @ResponseBody
    public ResultJson methodArgumentNotValidExceptionHandler(HttpServletRequest request, BindException e){
        log.warn("8、数据校验不通过，请求路径：{},异常错误信息:{}", request.getRequestURI(), e.getMessage());
        return ResultJson.error(getErrorMsg(e));
    }

    /**
     * 提取出错误信息
     * @param e 错误
     * @return
     */
    private String getErrorMsg(Exception e){
        //获取错误验证结果
        StringBuilder sb = new StringBuilder();

        if (e instanceof MethodArgumentNotValidException){
            MethodArgumentNotValidException exception = (MethodArgumentNotValidException) e;
            exception.getBindingResult().getFieldErrors().forEach((error) -> {
                sb.append(error.getDefaultMessage());
                sb.append(",");
            });
        } else if (e instanceof BindException) {
            BindException bindException = (BindException) e;
            bindException.getBindingResult().getFieldErrors().forEach((error) -> {
                //取出错误数据并封装
                sb.append(error.getDefaultMessage());
                sb.append(",");
            });
        }

        //去除最后一个逗号
        return sb.deleteCharAt(sb.length() - 1).toString();
    }
}
